﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections.ObjectModel;
using NetLib;
using System.Diagnostics.Contracts;
using System.ComponentModel;

namespace MetaTracker.Model
{
    internal partial class TrackerModel
    {
        #region Data Fetch Methods

        internal bool? FetchWeightWithings(bool forceUpdate = false)
        {
            if (CurrentUser == null || !HasWithings)
            {
                return false;
            }

            if (WeightData.Count < 1 || forceUpdate || WeightChangeMade)
            {
                WeightData = new ObservableCollection<IWeightResults>(
                    WithingsFetcher.FetchWithingsResults(CurrentUser.WithingsUserID,
                    CurrentUser.WithingsPublicKey));

                WeightChangeMade = false;
                return true;
            }

            return null;
        }

        internal bool? FetchWeightFatSecret(bool forceUpdate = false)
        {
            if (CurrentUser == null || !HasFatSecret)
            {
                return false;
            }

            if (WeightData.Count < 1 || forceUpdate || WeightChangeMade)
            {

                var todaysDateInt = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalDays;

                var temp = FatSecretFetcher.WeightGetMonth(CurrentUser.FatSecretOauthToken,
                    CurrentUser.FatSecretOauthSecret, true);


                temp.AddRange(FatSecretFetcher
                    .WeightGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 30)).ToString(), true));

                temp.AddRange(FatSecretFetcher
                    .WeightGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 60)).ToString(), true));

                temp.AddRange(FatSecretFetcher
                    .WeightGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 90)).ToString(), true));

                temp.AddRange(FatSecretFetcher
                    .WeightGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 120)).ToString(), true));

                WeightData = new ObservableCollection<NetLib.IWeightResults>(temp);

                WeightChangeMade = false;
                return true;
            }

            return null;
        }

        internal bool? FetchWeightFitbit(bool forceUpdate = false)
        {
            if (CurrentUser == null || !HasFitbit)
            {
                return false;
            }

            if (WeightData.Count < 1 || forceUpdate || WeightChangeMade)
            {

                var todaysDateInt = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalDays;



                var temp = FitbitFetcher.FetchWeight(CurrentUser.FitbitOauthToken, CurrentUser.FitibtOauthSecret);

                WeightData = new ObservableCollection<NetLib.IWeightResults>(temp);

                WeightChangeMade = false;
                return true;
            }

            return null;
        }

        internal bool? FetchFoodFatSecret(bool forceUpdate = false)
        {
            if (CurrentUser == null || !HasFatSecret)
            {
                return false;
            }

            if (FoodData.Count < 1 || forceUpdate || FoodChangeMade)
            {
                var todaysDateInt = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalDays;

                var temp = FatSecretFetcher.FoodEntriesGetMonth(CurrentUser.FatSecretOauthToken,
                    CurrentUser.FatSecretOauthSecret, true);


                temp.AddRange(FatSecretFetcher
                    .FoodEntriesGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 30)).ToString(), true));

                temp.AddRange(FatSecretFetcher
                    .FoodEntriesGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 60)).ToString(), true));

                temp.AddRange(FatSecretFetcher
                    .FoodEntriesGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 90)).ToString(), true));

                temp.AddRange(FatSecretFetcher
                    .FoodEntriesGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 120)).ToString(), true));

                FoodData = new ObservableCollection<IFoodIntakeResults>(temp);

                FoodChangeMade = false;
                return true;
            }

            return null;
        }

        internal bool? FetchFoodFitbit(bool forceUpdate = false)
        {
            if (CurrentUser == null || !HasFitbit)
            {
                return false;
            }

            if (FoodData.Count < 1 || forceUpdate || FoodChangeMade)
            {
                FoodData = new ObservableCollection<IFoodIntakeResults>(
                    FitbitFetcher.FetchFoodIntake(CurrentUser.FitbitOauthToken, CurrentUser.FitibtOauthSecret));

                FoodChangeMade = false;
                return true;
            }

            return null;
        }

        internal bool? FetchExerciseFatSecret(bool forceUpdate = false)
        {
            if (CurrentUser == null || !HasFatSecret)
            {
                return false;
            }

            if (ExerciseData.Count < 1 || forceUpdate || ExerciseChangeMade)
            {
                var todaysDateInt = (DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalDays;

                var temp = FatSecretFetcher.ExerciseEntriesGetMonth(CurrentUser.FatSecretOauthToken,
                    CurrentUser.FatSecretOauthSecret, true);

                temp.AddRange(FatSecretFetcher
                    .ExerciseEntriesGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 30)).ToString(), true));

                temp.AddRange(FatSecretFetcher
                    .ExerciseEntriesGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 60)).ToString(), true));

                temp.AddRange(FatSecretFetcher
                    .ExerciseEntriesGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 90)).ToString(), true));

                temp.AddRange(FatSecretFetcher
                    .ExerciseEntriesGetMonth(CurrentUser.FatSecretOauthToken, CurrentUser.FatSecretOauthSecret,
                    ((int)(todaysDateInt - 120)).ToString(), true));

                ExerciseData = new ObservableCollection<IExerciseResults>(temp);

                ExerciseChangeMade = false;
                return true;
            }

            return null;
        }

        internal bool? FetchExerciseFitbit(bool forceUpdate = false)
        {
            if (CurrentUser == null || !HasFitbit)
            {
                return false;
            }

            if (ExerciseData.Count < 1 || forceUpdate || ExerciseChangeMade)
            {
                ExerciseData = new ObservableCollection<IExerciseResults>(
                    FitbitFetcher.FetchExercise(CurrentUser.FitbitOauthToken, CurrentUser.FitibtOauthSecret));

                ExerciseChangeMade = false;
                return true;
            }

            return null;
        }



        internal bool? BuildStatistics(bool forceUpdate = false)
        {
            Contract.Requires(CurrentUser != null);
            Contract.Requires(WeightService != ServiceEnum.None);
            Contract.Requires(ExerciseService != ServiceEnum.None);
            Contract.Requires(FoodIntakeService != ServiceEnum.None);

            //Hardcoded services for now
            if (CurrentUser == null)
            {
                return false;
            }

            //Exercise updating
            if (forceUpdate || ExerciseChangeMade || ExerciseData.Count < 1)
            {
                if (ExerciseService == ServiceEnum.FatSecret)
                {
                    FetchExerciseFatSecret(forceUpdate);
                }
                else if (ExerciseService == ServiceEnum.Fitbit)
                {
                    FetchExerciseFitbit(forceUpdate);
                }
            }

            //Food Intake Updating
            if (forceUpdate || FoodChangeMade || FoodData.Count < 1)
            {
                if (FoodIntakeService == ServiceEnum.FatSecret)
                {
                    FetchFoodFatSecret(forceUpdate);
                }
                else if (FoodIntakeService == ServiceEnum.Fitbit)
                {
                    FetchFoodFitbit(forceUpdate);
                }
            }

            //Weight Updating
            switch (WeightService)
            {
                case ServiceEnum.Withings:
                    if (forceUpdate || WeightChangeMade || WeightData.Count < 1)
                    {
                        FetchWeightWithings(forceUpdate);
                    }
                    break;
                case ServiceEnum.FatSecret:
                    if (forceUpdate || WeightChangeMade || WeightData.Count < 1)
                    {
                        FetchWeightFatSecret(forceUpdate);
                    }
                    break;
                case ServiceEnum.Fitbit:
                    if (forceUpdate || WeightChangeMade || WeightData.Count < 1)
                    {
                        FetchWeightFitbit(forceUpdate);
                    }
                    break;
                default:
                    throw new InvalidEnumArgumentException("None cannot be selected for Weight Service for Statistics");
            }


            if (ExerciseData == null || ExerciseData.Count < 1)
                return null;
            if (FoodData == null || FoodData.Count < 1)
                return null;
            if (WeightData == null || WeightData.Count < 1)
            {
                return null;
            }


            //Find Start of first and last weeks
            DateTime startOfWeek = GetStartOfWeek(FoodData.Min(x => x.Date.Date));
            DateTime dateToTest = GetStartOfWeek(ExerciseData.Min(x => x.Date.Date));
            if (dateToTest < startOfWeek)
            {
                startOfWeek = dateToTest;
            }
            dateToTest = GetStartOfWeek(WeightData.Min(x => x.Date.Date));
            if (dateToTest < startOfWeek)
            {
                startOfWeek = dateToTest;
            }

            DateTime startOfEndDateWeek = GetStartOfWeek(DateTime.Now.Date);

            WeeklyStats = new ObservableCollection<WeeklyStatistics>();


            for (; startOfWeek.Date <= startOfEndDateWeek.Date; startOfWeek = startOfWeek.AddDays(7.00))
            {
                var endofweek = startOfWeek.AddDays(7.00);

                WeeklyStats.Add(new WeeklyStatistics(
                    startOfWeek,
                    WeightData
                        .Where(x => x.Date >= startOfWeek && x.Date < endofweek)
                        .Select(y => (double)y.Weight),
                    FoodData
                        .Where(z => z.Date.Date >= startOfWeek && z.Date.Date < endofweek)
                        .Select(a => a.CaloriesEaten),
                    ExerciseData
                        .Where(z => z.Date.Date >= startOfWeek && z.Date.Date < endofweek)
                        .Select(a => a.CaloriesBurned)
                        ));
            }

            //Do guestimate at differentiation!
            if (WeeklyStats.Count > 2)
            {
                WeeklyStats.First().ActualWeightChange =
                    (WeeklyStats[1].WeightAvg - WeeklyStats.First().WeightAvg);


                var lastindex = WeeklyStats.IndexOf(WeeklyStats.Last());
                WeeklyStats[lastindex].ActualWeightChange =
                    WeeklyStats[lastindex].WeightAvg - WeeklyStats[lastindex - 1].WeightAvg;

                //for all interior values
                for (int i = 1; i < WeeklyStats.Count - 1; i++)
                {
                    if ( WeeklyStats[i].WeightAvg == null || 
                        WeeklyStats[i - 1].WeightAvg == null && WeeklyStats[i + 1].WeightAvg == null)
                    {

                    }
                    else if (WeeklyStats[i - 1].WeightAvg == null && WeeklyStats[i + 1].WeightAvg != null)
                    {
                        WeeklyStats[i].ActualWeightChange =
                            (WeeklyStats[i + 1].WeightAvg - WeeklyStats[i].WeightAvg);
                    }
                    else if (WeeklyStats[i - 1].WeightAvg != null && WeeklyStats[i + 1].WeightAvg == null)
                    {
                        WeeklyStats[i].ActualWeightChange =
                            (WeeklyStats[i].WeightAvg - WeeklyStats[i - 1].WeightAvg);
                    }
                    else
                    {
                        WeeklyStats[i].ActualWeightChange =
                            (WeeklyStats[i + 1].WeightAvg - WeeklyStats[i - 1].WeightAvg) / 2;
                    }
                }
            }

            return true;

        }



        #endregion //Data Fetch Methods
    }
}
